/**=============================================================================
@file
   qhdsp_hvx_float_fft_internal.h

@brief
   Header file for common routines used internally in HVX implementations of float FFT.

Copyright (c) 2020 Qualcomm Technologies Incorporated.
All Rights Reserved. Qualcomm Proprietary and Confidential.
=============================================================================**/

#ifndef _QHDSP_HVX_FLOAT_FFT_INTERNAL_H
#define _QHDSP_HVX_FLOAT_FFT_INTERNAL_H

#ifdef __cplusplus
extern "C" {
#endif

#include "hvx_vector_register_defines.h"

/** @addtogroup qhdsp_hvx_float_fft_internal_routines QHDSP HVX FLOAT FFT internal routines
 *  @{
 */

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (sf x sf) - out sf
 *              Does complex multiplication of two 32 element float complex number vectors.
 * @param[in]   in1 - 1st input array of 32 float complex numbers
 * @param[in]   in2 - 2nd input array of 32 float complex numbers
 * @param[out]  out - [sf] output array of 32 float complex - result of float complex multiplication
 * @return      out in IEEE float format
 */
static inline void V_CPLX_MULT_sf_sf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -4);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V0, V2);          // a*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V1, V3);          // b*d
    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V4, V5);      // a*c-b*d
    V6 = Q6_Vsf_equals_Vqf32(V6);               // convert to IEEE float

    // imag part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V1, V2);          // b*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V0, V3);          // a*d
    V7 = Q6_Vqf32_vadd_Vqf32Vqf32(V4, V5);      // b*c+a*d
    V7 = Q6_Vsf_equals_Vqf32(V7);               // convert to IEEE float

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -4);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (sf x sf) - out qf32
 *              Does complex multiplication of two 32 element float complex number vectors.
 * @param[in]   in1 - 1st input array of 32 float complex numbers
 * @param[in]   in2 - 2nd input array of 32 float complex numbers
 * @param[out]  out - [qf32] output array of 32 float complex - result of float complex multiplication
 * @return      out in qf32 float format
 */
static inline void qf32_V_CPLX_MULT_sf_sf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -4);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V0, V2);          // a*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V1, V3);          // b*d
    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V4, V5);      // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V1, V2);          // b*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V0, V3);          // a*d
    V7 = Q6_Vqf32_vadd_Vqf32Vqf32(V4, V5);      // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -4);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (sf x sf) - out sf - conjugate
 *              Does complex multiplication of two 32 element float complex number vectors. Second input array is conjugated
 *              before doing complex multiplication.
 * @param[in]   in1 - 1st input array of 32 float complex numbers
 * @param[in]   in2 - 2nd input array of 32 float complex numbers
 * @param[out]  out - [sf] output array of 32 float complex - result of float complex multiplication
 * @return      out in IEEE float format
 */
static inline void V_CPLX_MULT_conj_sf_sf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V6 = Q6_V_vsplat_R(0x80000000);             // mask for negation - done with XOR operation

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -4);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // V2 - real part, V3 - imag part (2nd complex number array)

    V3 = Q6_V_vxor_VV(V3, V6);                  // conjugate

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V0, V2);          // a*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V1, V3);          // b*d
    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V4, V5);      // a*c-b*d
    V6 = Q6_Vsf_equals_Vqf32(V6);               // convert to IEEE float

    // imag part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V1, V2);          // b*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V0, V3);          // a*d
    V7 = Q6_Vqf32_vadd_Vqf32Vqf32(V4, V5);      // b*c+a*d
    V7 = Q6_Vsf_equals_Vqf32(V7);               // convert to IEEE float

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -4);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (sf x sf) - out qf32 - conjugate
 *              Does complex multiplication of two 32 element float complex number vectors. Second input array is conjugated
 *              before doing complex multiplication.
 * @param[in]   in1 - 1st input array of 32 float complex numbers
 * @param[in]   in2 - 2nd input array of 32 float complex numbers
 * @param[out]  out - [qf32] output array of 32 float complex - result of float complex multiplication
 * @return      out in qf32 float format
 */
static inline void qf32_V_CPLX_MULT_conj_sf_sf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V6 = Q6_V_vsplat_R(0x80000000);             // mask for negation - done with XOR operation

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -4);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // V2 - real part, V3 - imag part (2nd complex number array)

    V3 = Q6_V_vxor_VV(V3, V6);                  // conjugate

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V0, V2);          // a*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V1, V3);          // b*d
    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V4, V5);      // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf32_vmpy_VsfVsf(V1, V2);          // b*c
    V5 = Q6_Vqf32_vmpy_VsfVsf(V0, V3);          // a*d
    V7 = Q6_Vqf32_vadd_Vqf32Vqf32(V4, V5);      // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -4);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation
 * @param[in,out]   x - input/output buffer of 32 float complex values [sf] on which butterfly operation is done
 * @return
 */
static inline void sf_Radix4BTFLY_sf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_V_vsplat_R(0x80000000);             // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -8);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf32_vadd_VsfVsf(V2, V3);          // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf32_vsub_VsfVsf(V2, V3);          // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -8);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -8);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -4);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V6 = Q6_Vsf_equals_Vqf32(V6);       // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf32 doesn't work properly
                                        //                      thus, converting to IEEE float
    V6 = Q6_V_vxor_VV(V6, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V6 = Q6_Vqf32_vadd_VsfVsf(V6, V5);  // HACK/WORKAROUND! -   back to qf32

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -4);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -8);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -8);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf32_vadd_Vqf32Vqf32(V8, V6);      // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V8, V6);      // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -16);        // shuffle x[] results
    V2 = Q6_Vsf_equals_Vqf32(V2);               // convert to IEEE float
    V3 = Q6_Vsf_equals_Vqf32(V3);               // convert to IEEE float

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-2 FFT butterfly operation
 * @param[in,out]   x - input/output buffer of 32 float complex values [sf] on which butterfly operation is done
 * @return
 */
static inline void sf_Radix2BTFLY_sf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;

    V3_2 = *x;

    V1_0 = Q6_W_vdeal_VVR(V3, V2, -8);          // split to even and odd elements
    V2 = Q6_Vqf32_vadd_VsfVsf(V0, V1);
    V3 = Q6_Vqf32_vsub_VsfVsf(V0, V1);
    V1_0 = Q6_W_vshuff_VVR(V3, V2, -8);

    V0 = Q6_Vsf_equals_Vqf32(V0);               // convert to IEEE float
    V1 = Q6_Vsf_equals_Vqf32(V1);               // convert to IEEE float

    *x = V1_0;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 IFFT butterfly operation
 * @param[in,out]   x - input/output buffer of 32 float complex values [sf] on which butterfly operation is done
 * @return
 */
static inline void sf_IFFT_Radix4BTFLY_sf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_V_vsplat_R(0x80000000);             // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -8);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf32_vadd_VsfVsf(V2, V3);          // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf32_vsub_VsfVsf(V2, V3);          // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -8);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -8);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -4);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V7 = Q6_Vsf_equals_Vqf32(V7);       // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf32 doesn't work properly
                                        //                      thus, converting to IEEE float
    V7 = Q6_V_vxor_VV(V7, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V7 = Q6_Vqf32_vadd_VsfVsf(V7, V5);  // HACK/WORKAROUND! -   back to qf32

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -4);         // b[1] = L_complex(L_negate(L_imag(b[1])), L_real(b[1]));
                                                // b[3] = L_complex(L_negate(L_imag(b[3])), L_real(b[3])); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -8);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -8);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf32_vadd_Vqf32Vqf32(V8, V6);      // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V8, V6);      // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -16);        // shuffle x[] results
    V2 = Q6_Vsf_equals_Vqf32(V2);               // convert to IEEE float
    V3 = Q6_Vsf_equals_Vqf32(V3);               // convert to IEEE float

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @return
 */
static inline void sf_Radix4BTFLY_sf_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf32_vadd_VsfVsf(V0, V2);              // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf32_vadd_VsfVsf(V1, V3);              // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf32_vsub_VsfVsf(V0, V2);             // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf32_vsub_VsfVsf(V1, V3);             // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf32_vadd_VsfVsf(V4, V6);             // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf32_vadd_VsfVsf(V5, V7);             // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf32_vsub_VsfVsf(V4, V6);             // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf32_vsub_VsfVsf(V5, V7);             // d = L_Vsub(x[2], x[3]);

    V0 = Q6_V_vsplat_R(0x80000000);                 // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // -j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -4);          // split to real & imag parts
    V14 = Q6_Vsf_equals_Vqf32(V14);             // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf32 doesn't work properly
                                                //                      thus, converting to IEEE float

    V14 = Q6_V_vxor_VV(V14, V0);                    // L_negate(L_real(d))
    V14 = Q6_Vqf32_vadd_VsfVsf(V14, V1);        // HACK/WORKAROUND! -   back to qf32

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -4);         // d = L_complex(L_imag(d), L_negate(L_real(d)));

    V0 = Q6_Vqf32_vadd_Vqf32Vqf32(V8, V12);         //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf32_vadd_Vqf32Vqf32(V9, V13);         //x[0] = L_Vadd(a, c);
    V0 = Q6_Vsf_equals_Vqf32(V0);
    V1 = Q6_Vsf_equals_Vqf32(V1);
    *x0 = V1_0;

    V2 = Q6_Vqf32_vadd_Vqf32Vqf32(V10, V14);        //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf32_vadd_Vqf32Vqf32(V11, V15);        //x[1] = L_Vadd(b, d);
    V2 = Q6_Vsf_equals_Vqf32(V2);
    V3 = Q6_Vsf_equals_Vqf32(V3);
    *x1 = V3_2;

    V4 = Q6_Vqf32_vsub_Vqf32Vqf32(V8, V12);         //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf32_vsub_Vqf32Vqf32(V9, V13);         //x[2] = L_Vsub(a, c);
    V4 = Q6_Vsf_equals_Vqf32(V4);
    V5 = Q6_Vsf_equals_Vqf32(V5);
    *x2 = V5_4;

    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V10, V14);        //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf32_vsub_Vqf32Vqf32(V11, V15);        //x[3] = L_Vsub(b, d);
    V6 = Q6_Vsf_equals_Vqf32(V6);
    V7 = Q6_Vsf_equals_Vqf32(V7);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-2 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @return
 */
static inline void sf_Radix2BTFLY_sf_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;

    V1_0 = *x0;
    V3_2 = *x1;

    V4 = Q6_Vqf32_vadd_VsfVsf(V0, V2);              // a = L_Vadd(x[0], x[1]);
    V5 = Q6_Vqf32_vadd_VsfVsf(V1, V3);              // a = L_Vadd(x[0], x[1]);
    V6 = Q6_Vqf32_vsub_VsfVsf(V0, V2);              // b = L_Vsub(x[0], x[1]);
    V7 = Q6_Vqf32_vsub_VsfVsf(V1, V3);              // b = L_Vsub(x[0], x[1]);

    V4 = Q6_Vsf_equals_Vqf32(V4);                   // convert to IEEE float
    V5 = Q6_Vsf_equals_Vqf32(V5);                   // convert to IEEE float
    V6 = Q6_Vsf_equals_Vqf32(V6);                   // convert to IEEE float
    V7 = Q6_Vsf_equals_Vqf32(V7);                   // convert to IEEE float

    *x0 = V5_4;                                     //x[0] = a;
    *x1 = V7_6;                                     //x[1] = b;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 IFFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 32 float complex values on which butterfly operation is done
 * @return
 */
static inline void sf_IFFT_Radix4BTFLY_sf_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf32_vadd_VsfVsf(V0, V2);              // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf32_vadd_VsfVsf(V1, V3);              // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf32_vsub_VsfVsf(V0, V2);             // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf32_vsub_VsfVsf(V1, V3);             // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf32_vadd_VsfVsf(V4, V6);             // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf32_vadd_VsfVsf(V5, V7);             // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf32_vsub_VsfVsf(V4, V6);             // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf32_vsub_VsfVsf(V5, V7);             // d = L_Vsub(x[2], x[3]);

    V0 = Q6_V_vsplat_R(0x80000000);                 // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -4);          // split to real & imag parts
    V15 = Q6_Vsf_equals_Vqf32(V15);             // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf32 doesn't work properly
                                                //                      thus, converting to IEEE float

    V15 = Q6_V_vxor_VV(V15, V0);                    // L_negate(L_imag(d))
    V15 = Q6_Vqf32_vadd_VsfVsf(V15, V1);        // HACK/WORKAROUND! -   back to qf32

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -4);         // d = L_complex(L_negate(L_imag(d)), L_real(d));

    V0 = Q6_Vqf32_vadd_Vqf32Vqf32(V8, V12);         //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf32_vadd_Vqf32Vqf32(V9, V13);         //x[0] = L_Vadd(a, c);
    V0 = Q6_Vsf_equals_Vqf32(V0);
    V1 = Q6_Vsf_equals_Vqf32(V1);
    *x0 = V1_0;

    V2 = Q6_Vqf32_vadd_Vqf32Vqf32(V10, V14);        //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf32_vadd_Vqf32Vqf32(V11, V15);        //x[1] = L_Vadd(b, d);
    V2 = Q6_Vsf_equals_Vqf32(V2);
    V3 = Q6_Vsf_equals_Vqf32(V3);
    *x1 = V3_2;

    V4 = Q6_Vqf32_vsub_Vqf32Vqf32(V8, V12);         //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf32_vsub_Vqf32Vqf32(V9, V13);         //x[2] = L_Vsub(a, c);
    V4 = Q6_Vsf_equals_Vqf32(V4);
    V5 = Q6_Vsf_equals_Vqf32(V5);
    *x2 = V5_4;

    V6 = Q6_Vqf32_vsub_Vqf32Vqf32(V10, V14);        //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf32_vsub_Vqf32Vqf32(V11, V15);        //x[3] = L_Vsub(b, d);
    V6 = Q6_Vsf_equals_Vqf32(V6);
    V7 = Q6_Vsf_equals_Vqf32(V7);
    *x3 = V7_6;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (qf16 x hf) - out qf16
 *              Does complex multiplication of two 32 element qhl_cfloat16_t complex number vectors.
 * @param[in]   in1 - 1st input array of 32 qhl_cfloat16_t numbers
 * @param[in]   in2 - 2nd input array of 32 qhl_cfloat16_t numbers
 * @param[out]  out - [qf16] output array of 32 qhl_cfloat16_t - result of (half-precision) float complex multiplication
 * @return      out in qf16 format
 */
static inline void qf16_V_CPLX_MULT_qf16_hf_vect(HVX_Vector in1, HVX_Vector in2, HVX_Vector *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V4 = Q6_V_vzero();

    V1_0 = Q6_W_vdeal_VVR(V4, in1, -2);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V4, in2, -2);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V2);                // a*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V3);                // b*d
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V5);                  // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V2);                // b*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V3);                // a*d
    V7 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V5);                  // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -2);         // shuffle back real & imag parts together

    *out = V0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (qf16 x hf) - out qf16
 *              Does complex multiplication of two 64 element qhl_cfloat16_t complex number vectors.
 * @param[in]   in1 - 1st input array of 64 qhl_cfloat16_t numbers
 * @param[in]   in2 - 2nd input array of 64 qhl_cfloat16_t numbers
 * @param[out]  out - [qf16] output array of 64 qhl_cfloat16_t - result of (half-precision) float complex multiplication
 * @return      out in qf16 format
 */
static inline void qf16_V_CPLX_MULT_qf16_hf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -2);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -2);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V2);                // a*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V3);                // b*d
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V5);                  // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V2);                // b*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V3);                // a*d
    V7 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V5);                  // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -2);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (qf16 x hf) - out hf
 *              Does complex multiplication of two 64 element qhl_cfloat16_t complex number vectors.
 * @param[in]   in1 - 1st input array of 64 qhl_cfloat16_t numbers
 * @param[in]   in2 - 2nd input array of 64 qhl_cfloat16_t numbers
 * @param[out]  out - [hf] output array of 64 qhl_cfloat16_t - result of (half-precision) float complex multiplication
 * @return      out in hf (half-precision) float format
 */
static inline void hf_V_CPLX_MULT_qf16_hf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -2);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -2);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V2);                // a*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V3);                // b*d
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V5);                  // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V2);                // b*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V3);                // a*d
    V7 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V5);                  // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -2);         // shuffle back real & imag parts together

    V0 = Q6_Vhf_equals_Vqf16(V0);
    V1 = Q6_Vhf_equals_Vqf16(V1);

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (hf x hf) - out hf
 *              Does complex multiplication of two 64 element qhl_cfloat16_t complex number vectors.
 * @param[in]   in1 - 1st input array of 64 qhl_cfloat16_t numbers
 * @param[in]   in2 - 2nd input array of 64 qhl_cfloat16_t numbers
 * @param[out]  out - [hf] output array of 64 qhl_cfloat16_t - result of (half-precision) float complex multiplication
 * @return      out in hf (half-precision) float format
 */
static inline void hf_V_CPLX_MULT_hf_hf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -2);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -2);          // V2 - real part, V3 - imag part (2nd complex number array)

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf16_vmpy_VhfVhf(V0, V2);              // a*c
    V5 = Q6_Vqf16_vmpy_VhfVhf(V1, V3);              // b*d
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V5);                  // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf16_vmpy_VhfVhf(V1, V2);              // b*c
    V5 = Q6_Vqf16_vmpy_VhfVhf(V0, V3);              // a*d
    V7 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V5);                  // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -2);         // shuffle back real & imag parts together

    V0 = Q6_Vhf_equals_Vqf16(V0);
    V1 = Q6_Vhf_equals_Vqf16(V1);

    *out = V1_0;
}

/**
 * @brief       [HVX HELPER FUNCTION] Vector complex multiply (qf16 x hf) - out qf16 - conjugate
 *              Does complex multiplication of two 64 element qhl_cfloat16_t complex number vectors. Second input array is conjugated
 * @param[in]   in1 - 1st input array of 64 qhl_cfloat16_t numbers
 * @param[in]   in2 - 2nd input array of 64 qhl_cfloat16_t numbers
 * @param[out]  out - [qf16] output array of 64 qhl_cfloat16_t - result of (half-precision) float complex multiplication
 * @return      out in qf16 format
 */
static inline void qf16_V_CPLX_MULT_conj_qf16_hf(HVX_VectorPair in1, HVX_VectorPair in2, HVX_VectorPair *out)
{
    HVX_VP V1__0, V3__2, V5__4, V7__6;

    V1_0 = in1;
    V3_2 = in2;

    V6 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V1_0 = Q6_W_vdeal_VVR(V1, V0, -2);          // V0 - real part, V1 - imag part (1st complex number array)
    V3_2 = Q6_W_vdeal_VVR(V3, V2, -2);          // V2 - real part, V3 - imag part (2nd complex number array)

    V3 = Q6_V_vxor_VV(V3, V6);

    // (a+bi)*(c+di) = (a*c-b*d) + (b*c+a*d)i
    // real part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V2);                // a*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V3);                // b*d
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V5);                  // a*c-b*d

    // imag part of result
    V4 = Q6_Vqf16_vmpy_Vqf16Vhf(V1, V2);                // b*c
    V5 = Q6_Vqf16_vmpy_Vqf16Vhf(V0, V3);                // a*d
    V7 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V5);                  // b*c+a*d

    V1_0 = Q6_W_vshuff_VVR(V7, V6, -2);         // shuffle back real & imag parts together

    *out = V1_0;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation - qf16
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [qf16] on which butterfly operation is done
 * @return          return values in qf16 format
 */
static inline void qf16_Radix4BTFLY_qf16_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V2, V3);                  // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_Vqf16Vqf16(V2, V3);                  // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V6 = Q6_Vhf_equals_Vqf16(V6);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V6 = Q6_V_vxor_VV(V6, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V6 = Q6_Vqf16_vadd_VhfVhf(V6, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation - hf
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [qf16] on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix4BTFLY_qf16_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V2, V3);                  // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_Vqf16Vqf16(V2, V3);                  // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V6 = Q6_Vhf_equals_Vqf16(V6);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V6 = Q6_V_vxor_VV(V6, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V6 = Q6_Vqf16_vadd_VhfVhf(V6, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    V2 = Q6_Vhf_equals_Vqf16(V2);
    V3 = Q6_Vhf_equals_Vqf16(V3);

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation - qf16
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [hf] on which butterfly operation is done
 * @return          return values in qf16 format
 */
static inline void qf16_Radix4BTFLY_hf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_VhfVhf(V2, V3);              // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_VhfVhf(V2, V3);              // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V6 = Q6_Vhf_equals_Vqf16(V6);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V6 = Q6_V_vxor_VV(V6, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V6 = Q6_Vqf16_vadd_VhfVhf(V6, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 FFT butterfly operation - hf
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [hf] on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix4BTFLY_hf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_VhfVhf(V2, V3);              // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_VhfVhf(V2, V3);              // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V6 = Q6_Vhf_equals_Vqf16(V6);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V6 = Q6_V_vxor_VV(V6, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V6 = Q6_Vqf16_vadd_VhfVhf(V6, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    V2 = Q6_Vhf_equals_Vqf16(V2);
    V3 = Q6_Vhf_equals_Vqf16(V3);

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 IFFT butterfly operation - qf16
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [hf] on which butterfly operation is done
 * @return          return values in qf16 format
 */
static inline void qf16_IFFT_Radix4BTFLY_hf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_VhfVhf(V2, V3);              // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_VhfVhf(V2, V3);              // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V7 = Q6_Vhf_equals_Vqf16(V7);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V7 = Q6_V_vxor_VV(V7, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V7 = Q6_Vqf16_vadd_VhfVhf(V7, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_negate(L_imag(b[1])), L_real(b[1]));
                                                // b[3] = L_complex(L_negate(L_imag(b[3])), L_real(b[3])); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 IFFT butterfly operation - qf16
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [qf16] on which butterfly operation is done
 * @return          return values in qf16 format
 */
static inline void qf16_IFFT_Radix4BTFLY_qf16_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V2, V3);                  // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_Vqf16Vqf16(V2, V3);                  // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V7 = Q6_Vhf_equals_Vqf16(V7);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V7 = Q6_V_vxor_VV(V7, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V7 = Q6_Vqf16_vadd_VhfVhf(V7, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-4 IFFT butterfly operation - hf
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [qf16] on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_IFFT_Radix4BTFLY_qf16_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    V5 = Q6_V_vzero();
    V8 = Q6_Vh_vsplat_R(0x8000);                // mask for negation - done with XOR operation

    V3_2 = *x;

    V3_2 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
                                                // V2: even - x[0], x[2], ... ; V3: odd - x[1], x[3], ...

    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V2, V3);                  // even + odd = a[0] = L_Vadd(x[ 0], x[ 1]); a[1] = L_Vadd(x[ 2], x[ 3]); ...
    V0 = Q6_Vqf16_vsub_Vqf16Vqf16(V2, V3);                  // even - odd = b[0] = L_Vsub(x[ 0], x[ 1]); b[1] = L_Vsub(x[ 2], x[ 3]); ...

    V3_2 = Q6_W_vdeal_VVR(V5, V0, -4);          // split to even and odd elements of b[]
                                                // V2: even - b[0], b[2], ...; V3: odd - b[1], b[3], ...

    V1_0 = Q6_W_vdeal_VVR(V5, V1, -4);          // split to even and odd elements of a[]
                                                // V0: even - a[0], a[2], ...; V1: odd - a[1], a[3], ...

    V7_6 = Q6_W_vdeal_VVR(V5, V3, -2);          // split to real & imag parts of odd b[] elements (b[1], b[3])

    V7 = Q6_Vhf_equals_Vqf16(V7);           // HACK/WORKAROUND! -   for some reason, setting bit 31. in qf16 doesn't work properly
                                        //                      thus, converting to IEEE half-precision float
    V7 = Q6_V_vxor_VV(V7, V8);                  // L_negate(L_real(b[1])), L_negate(L_real(b[3])) ...
    V7 = Q6_Vqf16_vadd_VhfVhf(V7, V5);      // HACK/WORKAROUND! -   back to qf16

    V7_6 = Q6_W_vshuff_VVR(V6, V7, -2);         // b[1] = L_complex(L_imag(b[1]), L_negate(L_real(b[1])));
                                                // b[3] = L_complex(L_imag(b[3]), L_negate(L_real(b[3]))); ...

    V9_8 = Q6_W_vshuff_VVR(V2, V0, -4);         // shuffle even a[] and b[] parts
    V7_6 = Q6_W_vshuff_VVR(V6, V1, -4);         // shuffle odd a[] and b[] parts

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V6);                  // x[ 0] = L_Vadd(a[0], a[1]); x[ 1] = L_Vadd(b[0], b[1]);
                                                // x[ 4] = L_Vadd(a[2], a[3]); x[ 5] = L_Vadd(b[2], b[3]);

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V6);                  // x[ 2] = L_Vsub(a[0], a[1]); x[ 3] = L_Vsub(b[0], b[1]);
                                                // x[ 6] = L_Vsub(a[2], a[3]); x[ 7] = L_Vsub(b[2], b[3]);

    V3_2 = Q6_W_vshuff_VVR(V6, V4, -8);         // shuffle x[] results

    V2 = Q6_Vhf_equals_Vqf16(V2);
    V3 = Q6_Vhf_equals_Vqf16(V3);

    *x = V3_2;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-2 FFT butterfly operation
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [qf16] on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix2BTFLY_qf16_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;

    V3_2 = *x;

    V1_0 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V1);
    V3 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V1);
    V1_0 = Q6_W_vshuff_VVR(V3, V2, -4);

    V0 = Q6_Vhf_equals_Vqf16(V0);                   // convert to IEEE float
    V1 = Q6_Vhf_equals_Vqf16(V1);                   // convert to IEEE float

    *x = V1_0;
}

/**
 * @brief           [HVX HELPER FUNCTION] Float point Radix-2 FFT butterfly operation
 * @param[in,out]   x - input/output buffer of 64 qhl_cfloat16_t values [hf] on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix2BTFLY_hf_vect_pair(HVX_VectorPair* x)
{
    HVX_VP V1__0;
    HVX_VP V3__2;

    V3_2 = *x;

    V1_0 = Q6_W_vdeal_VVR(V3, V2, -4);          // split to even and odd elements
    V2 = Q6_Vqf16_vadd_VhfVhf(V0, V1);
    V3 = Q6_Vqf16_vsub_VhfVhf(V0, V1);
    V1_0 = Q6_W_vshuff_VVR(V3, V2, -4);

    V0 = Q6_Vhf_equals_Vqf16(V0);                   // convert to IEEE float
    V1 = Q6_Vhf_equals_Vqf16(V1);                   // convert to IEEE float

    *x = V1_0;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @return          out in qf16 format
 */
static inline void qf16_Radix4BTFLY_hf_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_VhfVhf(V0, V2);                  // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_VhfVhf(V1, V3);                  // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_VhfVhf(V0, V2);                 // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_VhfVhf(V1, V3);                 // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_VhfVhf(V4, V6);                 // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_VhfVhf(V5, V7);                 // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_VhfVhf(V4, V6);                 // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_VhfVhf(V5, V7);                 // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // -j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V14 = Q6_Vhf_equals_Vqf16(V14);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V14 = Q6_V_vxor_VV(V14, V0);                    // L_negate(L_real(d))
    V14 = Q6_Vqf16_vadd_VhfVhf(V14, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_imag(d), L_negate(L_real(d)));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 IFFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qhl_cfloat16_t values on which butterfly operation is done
 * @return          out in qf16 format
 */
static inline void qf16_IFFT_Radix4BTFLY_hf_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_VhfVhf(V0, V2);                  // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_VhfVhf(V1, V3);                  // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_VhfVhf(V0, V2);                 // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_VhfVhf(V1, V3);                 // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_VhfVhf(V4, V6);                 // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_VhfVhf(V5, V7);                 // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_VhfVhf(V4, V6);                 // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_VhfVhf(V5, V7);                 // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V15 = Q6_Vhf_equals_Vqf16(V15);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V15 = Q6_V_vxor_VV(V15, V0);                    // L_negate(L_imag(d))
    V15 = Q6_Vqf16_vadd_VhfVhf(V15, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_negate(L_imag(d)), L_real(d));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @return          out in qf16 format
 */
static inline void qf16_Radix4BTFLY_qf16_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V2);                      // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_Vqf16Vqf16(V1, V3);                      // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V2);                     // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_Vqf16Vqf16(V1, V3);                     // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V6);                     // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_Vqf16Vqf16(V5, V7);                     // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V6);                     // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_Vqf16Vqf16(V5, V7);                     // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // -j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V14 = Q6_Vhf_equals_Vqf16(V14);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V14 = Q6_V_vxor_VV(V14, V0);                    // L_negate(L_real(d))
    V14 = Q6_Vqf16_vadd_VhfVhf(V14, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_imag(d), L_negate(L_real(d)));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 IFFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @return          out in qf16 format
 */
static inline void qf16_IFFT_Radix4BTFLY_qf16_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V2);                      // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_Vqf16Vqf16(V1, V3);                      // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V2);                     // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_Vqf16Vqf16(V1, V3);                     // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V6);                     // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_Vqf16Vqf16(V5, V7);                     // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V6);                     // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_Vqf16Vqf16(V5, V7);                     // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V15 = Q6_Vhf_equals_Vqf16(V15);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V15 = Q6_V_vxor_VV(V15, V0);                    // L_negate(L_imag(d))
    V15 = Q6_Vqf16_vadd_VhfVhf(V15, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_negate(L_imag(d)), L_real(d));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix4BTFLY_qf16_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V2);                      // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_Vqf16Vqf16(V1, V3);                      // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V2);                     // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_Vqf16Vqf16(V1, V3);                     // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V6);                     // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_Vqf16Vqf16(V5, V7);                     // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V6);                     // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_Vqf16Vqf16(V5, V7);                     // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // -j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V14 = Q6_Vhf_equals_Vqf16(V14);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V14 = Q6_V_vxor_VV(V14, V0);                    // L_negate(L_real(d))
    V14 = Q6_Vqf16_vadd_VhfVhf(V14, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_imag(d), L_negate(L_real(d)));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    V0 = Q6_Vhf_equals_Vqf16(V0);
    V1 = Q6_Vhf_equals_Vqf16(V1);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    V2 = Q6_Vhf_equals_Vqf16(V2);
    V3 = Q6_Vhf_equals_Vqf16(V3);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    V4 = Q6_Vhf_equals_Vqf16(V4);
    V5 = Q6_Vhf_equals_Vqf16(V5);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    V6 = Q6_Vhf_equals_Vqf16(V6);
    V7 = Q6_Vhf_equals_Vqf16(V7);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-4 IFFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x2 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x3 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_IFFT_Radix4BTFLY_qf16_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1, HVX_VectorPair* x2, HVX_VectorPair* x3)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;
    HVX_VP V9__8;
    HVX_VP V11__10;
    HVX_VP V13__12;
    HVX_VP V15__14;

    V1_0 = *x0;
    V3_2 = *x1;
    V5_4 = *x2;
    V7_6 = *x3;

    V8 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V2);                      // a = L_Vadd(x[0], x[1]);
    V9 = Q6_Vqf16_vadd_Vqf16Vqf16(V1, V3);                      // a = L_Vadd(x[0], x[1]);
    V10 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V2);                     // b = L_Vsub(x[0], x[1]);
    V11 = Q6_Vqf16_vsub_Vqf16Vqf16(V1, V3);                     // b = L_Vsub(x[0], x[1]);
    V12 = Q6_Vqf16_vadd_Vqf16Vqf16(V4, V6);                     // c = L_Vadd(x[2], x[3]);
    V13 = Q6_Vqf16_vadd_Vqf16Vqf16(V5, V7);                     // c = L_Vadd(x[2], x[3]);
    V14 = Q6_Vqf16_vsub_Vqf16Vqf16(V4, V6);                     // d = L_Vsub(x[2], x[3]);
    V15 = Q6_Vqf16_vsub_Vqf16Vqf16(V5, V7);                     // d = L_Vsub(x[2], x[3]);

    V0 = Q6_Vh_vsplat_R(0x8000);                    // mask for negation - done with XOR operation
    V1 = Q6_V_vzero();

    // j*d
    V15_14 = Q6_W_vdeal_VVR(V15, V14, -2);          // split to real & imag parts
    V15 = Q6_Vhf_equals_Vqf16(V15);                 // HACK/WORKAROUND! -   for some reason, setting bit 15. in qf16 doesn't work properly
                                                //                      thus, converting to IEEE float

    V15 = Q6_V_vxor_VV(V15, V0);                    // L_negate(L_imag(d))
    V15 = Q6_Vqf16_vadd_VhfVhf(V15, V1);            // HACK/WORKAROUND! -   back to qf16

    V15_14 = Q6_W_vshuff_VVR(V14, V15, -2);         // d = L_complex(L_negate(L_imag(d)), L_real(d));

    V0 = Q6_Vqf16_vadd_Vqf16Vqf16(V8, V12);                     //x[0] = L_Vadd(a, c);
    V1 = Q6_Vqf16_vadd_Vqf16Vqf16(V9, V13);                     //x[0] = L_Vadd(a, c);
    V0 = Q6_Vhf_equals_Vqf16(V0);
    V1 = Q6_Vhf_equals_Vqf16(V1);
    *x0 = V1_0;

    V2 = Q6_Vqf16_vadd_Vqf16Vqf16(V10, V14);                    //x[1] = L_Vadd(b, d);
    V3 = Q6_Vqf16_vadd_Vqf16Vqf16(V11, V15);                    //x[1] = L_Vadd(b, d);
    V2 = Q6_Vhf_equals_Vqf16(V2);
    V3 = Q6_Vhf_equals_Vqf16(V3);
    *x1 = V3_2;

    V4 = Q6_Vqf16_vsub_Vqf16Vqf16(V8, V12);                     //x[2] = L_Vsub(a, c);
    V5 = Q6_Vqf16_vsub_Vqf16Vqf16(V9, V13);                     //x[2] = L_Vsub(a, c);
    V4 = Q6_Vhf_equals_Vqf16(V4);
    V5 = Q6_Vhf_equals_Vqf16(V5);
    *x2 = V5_4;

    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V10, V14);                    //x[3] = L_Vsub(b, d);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V11, V15);                    //x[3] = L_Vsub(b, d);
    V6 = Q6_Vhf_equals_Vqf16(V6);
    V7 = Q6_Vhf_equals_Vqf16(V7);
    *x3 = V7_6;
}

/**
 * @brief           [HVX HELPER FUNCTION] Half-precision float point Radix-2 FFT butterfly operation - column
 * @param[in,out]   x0 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @param[in,out]   x1 - input/output buffer of 64 qf16 complex values on which butterfly operation is done
 * @return          return values in IEEE half-precision float format (hf)
 */
static inline void hf_Radix2BTFLY_qf16_vect_pair_column(HVX_VectorPair* x0, HVX_VectorPair* x1)
{
    HVX_VP V1__0;
    HVX_VP V3__2;
    HVX_VP V5__4;
    HVX_VP V7__6;

    V1_0 = *x0;
    V3_2 = *x1;

    V4 = Q6_Vqf16_vadd_Vqf16Vqf16(V0, V2);                      // a = L_Vadd(x[0], x[1]);
    V5 = Q6_Vqf16_vadd_Vqf16Vqf16(V1, V3);                      // a = L_Vadd(x[0], x[1]);
    V6 = Q6_Vqf16_vsub_Vqf16Vqf16(V0, V2);                      // b = L_Vsub(x[0], x[1]);
    V7 = Q6_Vqf16_vsub_Vqf16Vqf16(V1, V3);                      // b = L_Vsub(x[0], x[1]);

    V4 = Q6_Vhf_equals_Vqf16(V4);                       // convert to IEEE float
    V5 = Q6_Vhf_equals_Vqf16(V5);                       // convert to IEEE float
    V6 = Q6_Vhf_equals_Vqf16(V6);                       // convert to IEEE float
    V7 = Q6_Vhf_equals_Vqf16(V7);                       // convert to IEEE float

    *x0 = V5_4;                                     //x[0] = a;
    *x1 = V7_6;                                     //x[1] = b;
}

/**
 * @brief           [HVX] Complex 1D 2^N single precision float point FFT - column
 *                  Performs column FFT on 32 columns at a time.
 * @param[in]       input - input (32) columns (float complex)
 * @param[in]       N - number of rows on which FFT is performed
 * @param[in]       w - twiddle factors
 * @param[out]      output - FFT output (32 output columns) buffer (float complex)
 * @note
 *                  - Assumptions:
 *                                  1. input, w, output - buffer aligned by 128bytes
 *                                  2. w - generated with qhdsp_hvx_fft_gen_twiddles_complex_vcf() function
 *                                  3. N is power of 2 and N>=32
 *                                  4. input rows already in bit-reversed order
 */
int32_t qhdsp_hvx_c1dfft_column_af(const float complex *input, uint32_t N, const float complex *w, float complex *output);

/**
 * @brief           [HVX] Complex 1D 2^N single precision float point IFFT - column
 *                  Performs column IFFT on 32 columns at a time.
 * @param[in]       input - input (32) columns (float complex)
 * @param[in]       N - number of rows on which IFFT is performed
 * @param[in]       w - twiddle factors
 * @param[out]      output - IFFT output (32 output columns) buffer (float complex)
 * @note
 *                  - Assumptions:
 *                                  1. input, w, output - buffer aligned by 128bytes
 *                                  2. w - generated with qhdsp_hvx_fft_gen_twiddles_complex_vcf() function
 *                                  3. N is power of 2 and N>=32
 */
int32_t qhdsp_hvx_c1difft_column_af(const float complex *input, uint32_t N, const float complex *w, float complex *output);

/**
 * @brief           [HVX] Complex 1D 2^N half precision float point FFT - column
 *                  Performs column FFT on 64 columns at a time.
 * @param[in]       input - input (64) columns (complex)
 * @param[in]       N - number of rows on which FFT is performed
 * @param[in]       w - twiddle factors
 * @param[out]      output - FFT output (64 output columns) buffer (complex)
 * @note
 *                  - Assumptions:
 *                                  1. input, w, output - buffer aligned by 128bytes
 *                                  2. w - generated with qhdsp_hvx_fft_gen_twiddles_complex_vchf() function
 *                                  3. N is power of 2 and N>=64
 *                                  4. input rows already in bit-reversed order
 */
int32_t qhdsp_hvx_c1dfft_column_ahf(const qhl_cfloat16_t *input, uint32_t N, const qhl_cfloat16_t *w, qhl_cfloat16_t *output);

/**
 * @brief           [HVX] Complex 1D 2^N half precision float point IFFT - column
 *                  Performs column IFFT on 64 columns at a time.
 * @param[in]       input - input (64) columns (complex)
 * @param[in]       N - number of rows on which IFFT is performed
 * @param[in]       w - twiddle factors
 * @param[out]      output - IFFT output (64 output columns) buffer (complex)
 * @note
 *                  - Assumptions:
 *                                  1. input, w, output - buffer aligned by 128bytes
 *                                  2. w - generated with qhdsp_hvx_fft_gen_twiddles_complex_vchf() function
 *                                  3. N is power of 2 and N>=64
 */
int32_t qhdsp_hvx_c1difft_column_ahf(const qhl_cfloat16_t *input, uint32_t N, const qhl_cfloat16_t *w, qhl_cfloat16_t *output);

/**
  * @}
  */

#ifdef __cplusplus
}
#endif

#endif /* _QHDSP_HVX_FLOAT_FFT_INTERNAL_H */
